﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using CS.Diagnostics;

namespace CS.Beehive
{
    /// <summary>
    /// 信道,通过包装socket进行一个简单的数据发送和接收处理工作 
    /// <remarks>
    /// 1. 关联Socekt上下文对象SocketAsyncEventArgs的TcpChannel
    /// 2. TcpCahnnel 异常时，Server端应断开该连接
    /// </remarks>
    /// </summary>
    public class TcpChannel : Session, ITcpChannel, IDisposable
    {

        #region 变量 属性


        public TcpServer Server { get; set; }

        public Socket ChannelSocket { get; private set; }

        public bool IsLittleEndian { get; set; }






        //*****************protected*************************//

        /// <summary>
        /// 缓冲区大小
        /// </summary>
        protected readonly int ReceiveBufferSize;
        /// <summary>
        /// 读池
        /// </summary>
        protected readonly SocketAsyncEventArgsPool ReaderPool;
        /// <summary>
        /// 写池
        /// </summary>
        protected readonly SocketAsyncEventArgsPool WriterPool;



        //*****************internal*************************//

        /// <summary>
        /// 关闭，中断原因
        /// </summary>
        internal string CloseReason { get; set; }

        /// <summary>
        /// 是否正释放资源
        /// </summary>
        internal bool Releasing { get; set; }
        /// <summary>
        /// 是否已释放资源
        /// </summary>
        internal bool IsDisposed { get; set; }


        #endregion


        #region 构造、初始化 、析构、资源释放


        /// <summary>
        /// 创建一个连接通道
        /// </summary>
        /// <param name="clientId">连接编号</param>
        /// <param name="channelSocket">与客户相连的Socket</param>
        /// <param name="tcpServer">通信连接的服务实例</param>
        public TcpChannel(long clientId, Socket channelSocket, TcpServer tcpServer)
        {
            ClientId = clientId;
            ChannelSocket = channelSocket;
            EndPoint = ChannelSocket.RemoteEndPoint as IPEndPoint;

            Server = tcpServer;
            ReceiveBufferSize = tcpServer.BufferSize;
            //ReceiveBuffer = new ReceiveBuffer(ReceiveBufferSize); //在连接初始化时启用，TODO:改进为读取对像
            ActiveTickCount = Environment.TickCount;

            //TODO:IMPROVE
            ReaderPool = new SocketAsyncEventArgsPool(2);
            WriterPool = new SocketAsyncEventArgsPool(4);
        }


        /// <summary>
        /// 正常断开连接
        /// </summary>
        public void Disconnect()
        {
            Release();
        }

        #region Close IDisposable

        /// <summary>
        /// 资源释放
        /// </summary>
        private void Release()
        {
            Releasing = true;
            Dispose(true);
        }


        #region Dispose 模式 (必须使用该模式式，否则无法获取断开事件)

        /// <summary>
        /// 关闭并断开SocketChannel连接
        /// <remarks>Release instance</remarks>
        /// </summary>
        public void Dispose()
        {
            //必须为true
            Dispose(true);
            //通知垃圾回收机制不再调用终结器（析构器）
            GC.SuppressFinalize(this);

        }

        /// <summary>
        /// 回收器的回收
        /// </summary>
        ~TcpChannel()
        {
            //必须为false
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
            lock (this)
            {
                if (IsDisposed) return;
                var args = new ChannelDisconnectEventArgs(this) { IsClean = true };
                ActiveTickCount = Environment.TickCount;
                try
                {
                    OnChannelDisconnected(args);//引发事件
                }
                catch (Exception ex)
                {
                    Tracer.Error(ex.Message);
                }
                if (args.IsClean)
                {
                    args.Channel = null;
                }
                else
                {
                    //Server.DisconnectMission.Add(args);
                }

                // 清理托管资源
                if (disposing)
                {
                    ClientId = -1;
                    ChannelSocket = null;
                    Server = null;
                    Abandon();

                    //清理读写池
                    WriterPool.Dispose();
                    ReaderPool.Dispose();
                }

                // 清理非托管资源
                TcpSetting.ReleaseSocket(ChannelSocket);

                IsDisposed = true;
            }
        }


        #endregion






        #endregion


        #endregion


        #region 事件

        ///// <summary>
        ///// TcpChannel 开始断开时
        ///// </summary>
        //public event EventHandler<ChannelDisconnectEventArgs> ChannelDisconnecting;
        ///// <summary>
        ///// TcpChannel g开始断开 调用
        ///// </summary>
        ///// <param name="e"></param>
        //protected virtual void OnChannelDisconnecting(ChannelDisconnectEventArgs e)
        //{
        //    if(ChannelDisconnecting == null)return;
        //    ChannelDisconnecting(this, e);
        //}
        /// <summary>
        /// Tcp断开连接完成
        /// </summary>
        public event EventHandler<ChannelDisconnectEventArgs> ChannelDisconnected;
        /// <summary>
        /// 完成释放并断开连接时调用
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnChannelDisconnected(ChannelDisconnectEventArgs e)
        {
            if (ChannelDisconnected == null) return;
            ChannelDisconnected(this, e);
        }

        /// <summary>
        /// 收到数据 事件
        /// <remarks>
        /// SocketAsyncEventArgs.UserToken中携带有客户端Id，相关信息
        /// </remarks>
        /// </summary>
        public event EventHandler<SocketAsyncEventArgs> DataReceived;
        /// <summary>
        /// 收到数据  时调用
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnDataReceived(SocketAsyncEventArgs e)
        {
            if (DataReceived == null) return;
            DataReceived(this, e);
        }

        /// <summary>
        /// 异常事件
        /// </summary>
        public event EventHandler<TcpExceptionEventArgs> TcpException;
        /// <summary>
        /// 异常发生时调用
        /// </summary>
        /// <param name="ex"></param>
        protected virtual void OnTcpException(Exception ex)
        {
            if (TcpException == null) return;
            TcpException(this, new TcpExceptionEventArgs(ex));
        }
        /// <summary>
        /// 异常发生时调用
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnTcpException(TcpExceptionEventArgs e)
        {
            if (TcpException == null) return;
            TcpException(this, e);
        }

        #endregion



        #region 公有方法

        public string GetCloseReason()
        {
            return CloseReason;
        }

        public void OnChannelException(TcpExceptionEventArgs e)
        {
            OnTcpException(e);
        }

        public void BeginReceive()
        {
            Receive();
        }

        /// <summary>
        /// 开始接受
        /// </summary>
        internal void Receive()
        {
            var reader = ReaderPool.Pop();
            if (reader == null)
            {
                reader = new SocketAsyncEventArgs();
                reader.Completed += (s, e) => ProcessReceive(e);
            }
            reader.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize);
            try
            {
                var isAsync = ChannelSocket.ReceiveAsync(reader);
                if (!isAsync) ProcessReceive(reader);   //同步完成
            }
            catch (Exception ex)
            {
                reader.Dispose();
                OnTcpException(ex);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="datagram">不为空的包文</param>
        public void Send(byte[] datagram)
        {
            var writer = WriterPool.Pop();
            if (writer == null)
            {
                writer = new SocketAsyncEventArgs();
                writer.Completed += (s, e) => { Interlocked.Add(ref Server.SentByteSize, datagram.Length); ProcessSend(e); };
            }
            writer.SetBuffer(datagram, 0, datagram.Length);
            try
            {
                if (ChannelSocket == null) return;
                var willRaiseEvent = ChannelSocket.SendAsync(writer);
                if (!willRaiseEvent) ProcessSend(writer);
            }
            catch (Exception ex)
            {
                writer.Dispose();
                OnTcpException(ex);
            }
        }


        #endregion


        #region 相关处理

        protected void OnCompleted(SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                default:
                    if (e.SocketError != SocketError.Success)
                    {
                        CloseReason = string.Format("OnCompleted SocketError:{0}", e.SocketError);
                        Dispose();
                    }
                    break;
            }
        }

        /// <summary>
        /// 处理数据接收
        /// </summary>
        /// <param name="context"></param>
        private void ProcessReceive(SocketAsyncEventArgs context)
        {
            try
            {
                ActiveTickCount = Environment.TickCount;
                Interlocked.Increment(ref Server.ReceiveIO);
                //检测客户端是否关闭
                if (context.BytesTransferred > 0 && context.SocketError == SocketError.Success && !IsDisposed)
                {
                    Interlocked.Add(ref Server.ReceivedByteSize, context.BytesTransferred);

                    //ReceiveBuffer.Set(context.Buffer, context.Offset, context.BytesTransferred);
                    OnDataReceived(context);
                    //byte[] buffer = BufferManager.TakeBuffer(e.BytesTransferred);
                    var isAsync = ChannelSocket.ReceiveAsync(context);//接受下批数据
                    if (!isAsync) ProcessReceive(context);//同步完成，立即处理
                }
                else
                {
                    //连接断开
                    context.Dispose();
                    Release();
                }
            }
            catch (Exception ex)
            {
                context.Dispose();
                Release();
                OnTcpException(ex);
            }
        }

        /// <summary>
        /// 发送处理
        /// </summary>
        /// <param name="context"></param>
        private void ProcessSend(SocketAsyncEventArgs context)
        {
            try
            {
                Interlocked.Increment(ref Server.SendIO);
                if (context.SocketError == SocketError.Success)
                {
                    WriterPool.Push(context);//回收
                }
                else
                {
                    context.Dispose();
                    Release();
                }
            }
            catch (Exception ex)
            {
                context.Dispose();
                OnTcpException(ex);
            }
        }


        #endregion

    }
}